namespace keyofFn {

    // keyof 运算符采用对象类型并生成其键的字符串或数字字面联合。返回对象的key值

    type Point = { x: number; y: number };

    type P = keyof Point;
    let s: P;
    s = "x"
    // keyof 遍历类型会返回方法
    type k = keyof number;
    let sss: k = "toString";


// ! 注意这个特殊
    type aany = keyof any; //string | number | symbol

    /*
  ^  在泛型约束中使用类型参数
  ^  你可以声明受另一个类型参数约束的类型参数。例如，在这里我们想从一个给定名称的对象中获取一个属性。
  ^  我想确保我们不会意外获取 obj 上不存在的属性，因此我们将在两种类型之间放置约束：
   */
    function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key): Type[Key] {
        return obj[key];
    }

    let xxxx = {a: 1, b: 2, c: 3, d: 4};
    let zz = getProperty(xxxx, "a");

    // ! getProperty(xxxx, "ee");报错这个key不在对象内


    // * 如果该类型具有 string 或 number 索引签名，则 keyof 将返回这些类型：
    type Arrayish = { [n: number]: unknown };
    type AS = keyof Arrayish;
    let sdasd: AS = 40;
    // let sdasd:A='asd';


    // ! 请注意，在这个例子中，M 是 string | number -
    // ! 这是因为 JavaScript 对象键总是被强制转换为字符串，所以 obj[0] 总是与 obj["0"] 相同。
    type Mapish = { [k: string]: boolean };
    type MS = keyof Mapish;
    let m: MS = "asd";
    m = 0
}


type ooo = {
    a: { a: string, b: string },
    b: { c: string, d: string },
}

type ov<T> = T[keyof T]

type asda = ov<ooo>